#include "pch.hpp"
#include "TextureAtlas.hpp"




Atlas::Atlas(const GlyphPlacements& glyphs)
	: m_glyphs(glyphs)
{
	calculateDimensions();
	pack();
	paint();
}
///////////////////////////////////////////////////////////////////////////////////////////////////

QImage Atlas::image() const
{
	return m_image;
}
///////////////////////////////////////////////////////////////////////////////////////////////////

void Atlas::calculateDimensions()
{
	int area = 0;
	int maxWidth = 0;
	int maxHeight = 0;

	foreach(const GlyphPlacement& glyph, m_glyphs)
	{
		area     += glyph.size.width * glyph.size.height;
		maxWidth  = std::max(maxWidth, glyph.size.width);
		maxHeight = std::max(maxHeight, glyph.size.height);
	}

	int squareRoot = math::sqrt(area);

	// TODO: Find closest power of 2 value, equal or greater to N...
	m_size.width  = std::max(squareRoot, maxWidth);
	m_size.height = std::max(squareRoot, maxHeight);
}
///////////////////////////////////////////////////////////////////////////////////////////////////

void Atlas::pack()
{
	struct compare
	{
		bool operator()(const GlyphPlacement& left, const GlyphPlacement& right)
		{
			// return left.area < right.area...
			return (left.size.width * left.size.height) > (right.size.width * right.size.height);
		}
	};

	std::sort(m_glyphs.begin(), m_glyphs.end(), compare());

	while(true)
	{
		m_placedGlyphs.clear();

		foreach(GlyphPlacement& glyph, m_glyphs)
		{
			glyph.pos = int2(-1, -1);
		}

		int successCount = 0;
		fill(0, 0, m_size.width - 1, m_size.height - 1, successCount);

		if(successCount == m_glyphs.size())
			break;

		static bool toggle = false;
		toggle = !toggle;

		// TODO: Find closest power of 2 value, equal or greater to N...
		if(toggle)
			m_size.width = m_size.width + 1;
		else
			m_size.height = m_size.height + 1;
	}
}
///////////////////////////////////////////////////////////////////////////////////////////////////

void Atlas::fill(int x1, int y1, int x2, int y2, int& count)
{
	GlyphPlacements::iterator i;

	for(i = m_glyphs.begin(); i != m_glyphs.end(); i++)
	{
		bool placed = m_placedGlyphs.contains(i->glyph);
		if((!placed) && (x2 - x1 + 1 >= i->size.width) && (y2 - y1 + 1 >= i->size.height))
			break;
	}

	if(i == m_glyphs.end())
		return;

	GlyphPlacement& glyph = *i;

	glyph.pos = int2(x1, y1);
	m_placedGlyphs.insert(i->glyph);
	count++;

	int aRight = (x2 - x1 + 1 - glyph.size.width) * glyph.size.height;
	int aDown  = (y2 - y1 + 1 - glyph.size.height) * glyph.size.width;
	if(aRight < aDown)
	{
		if(y1 + glyph.size.height < y2)
			fill(x1, y1 + glyph.size.height, x2, y2, count);

		if((x1 + glyph.size.width < x2) && (y1 < y1 + glyph.size.height - 1))
			fill(x1 + glyph.size.width, y1, x2, y1 + glyph.size.height - 1, count);
	}
	else
	{
		if(x1 + glyph.size.width < x2)
			fill(x1 + glyph.size.width, y1, x2, y2, count);

		if((y1 + glyph.size.height < y2) && (x1 < x1 + glyph.size.width - 1))
			fill(x1, y1 + glyph.size.height, x1 + glyph.size.width - 1, y2, count);
	}
}
///////////////////////////////////////////////////////////////////////////////////////////////////

void Atlas::paint()
{
	m_image = QImage(m_size.width, m_size.height, QImage::Format_ARGB32);
	m_image.fill(qRgba(0, 0, 0, 0));
	QPainter painter(&m_image);
	painter.setCompositionMode(QPainter::CompositionMode_Plus);

	for(GlyphPlacements::iterator i = m_glyphs.begin(); i != m_glyphs.end(); ++i)
	{
		GlyphPlacement& glyph = *i;
		AdvancedGlyph*  g = glyph.glyph;
		QImage image = g->image();
		QRect imageRect = image.rect();
		QRect srcRect;
		QRect destRect;

		// We've specified a wrong size in the placement because we will draw a border around the glyph to
		// minimize sampling (due to scaling) artifacts during rendering

		// Draw the entire source image over the destination, using the calculated values
		destRect = QRect(glyph.pos.x + 1, glyph.pos.y + 1, imageRect.width(), imageRect.height());
		painter.drawImage(destRect, image, imageRect);


		//
		// Now draw the 4 borders
		//

		// Top
		destRect = QRect(glyph.pos.x + 1, glyph.pos.y, imageRect.width(), 1);
		srcRect  = QRect(imageRect.x(), imageRect.y(), imageRect.width(), 1);
		painter.drawImage(destRect, image, srcRect);

		// Bottom
		destRect = QRect(glyph.pos.x + 1, glyph.pos.y + imageRect.height() + 1, imageRect.width(), 1);
		srcRect  = QRect(imageRect.x(), imageRect.y() + imageRect.height() - 1, imageRect.width(), 1);
		painter.drawImage(destRect, image, srcRect);

		// Left
		destRect = QRect(glyph.pos.x, glyph.pos.y + 1, 1, imageRect.height());
		srcRect  = QRect(imageRect.x(), imageRect.y(), 1, imageRect.height());
		painter.drawImage(destRect, image, srcRect);

		// Right
		destRect = QRect(glyph.pos.x + imageRect.width() + 1, glyph.pos.y + 1, 1, imageRect.height());
		srcRect  = QRect(imageRect.x() + imageRect.width() - 1, imageRect.y(), 1, imageRect.height());
		painter.drawImage(destRect, image, srcRect);

		//
		// And now draw the 4 corners
		//

		// Upper left
		destRect = QRect(glyph.pos.x, glyph.pos.y, 1, 1);
		srcRect  = QRect(imageRect.x(), imageRect.y(), 1, 1);
		painter.drawImage(destRect, image, srcRect);

		// Upper right
		destRect = QRect(glyph.pos.x + imageRect.width() + 1, glyph.pos.y, 1, 1);
		srcRect  = QRect(imageRect.x() + imageRect.width() - 1, imageRect.y(), 1, 1);
		painter.drawImage(destRect, image, srcRect);

		// Lower Right
		destRect = QRect(glyph.pos.x + imageRect.width() + 1, glyph.pos.y + imageRect.height() + 1, 1, 1);
		srcRect  = QRect(imageRect.x() + imageRect.width() - 1, imageRect.y() + imageRect.height() - 1, 1, 1);
		painter.drawImage(destRect, image, srcRect);

		// Lower Left
		destRect = QRect(glyph.pos.x, glyph.pos.y + imageRect.height() + 1, 1, 1);
		srcRect  = QRect(imageRect.x(), imageRect.y() + imageRect.height() - 1, 1, 1);
		painter.drawImage(destRect, image, srcRect);



		// Tell the glyph about its final position
		g->setPos(int2(glyph.pos.x + 1, glyph.pos.y + 1));
	}
}
///////////////////////////////////////////////////////////////////////////////////////////////////
